package 加密与安全;

/*
	公开密钥密码学（英语：Public-key cryptography）也称非对称式密码学（英语：Asymmetric cryptography）是密码学的一种
算法，它需要两个密钥，一个是公开密钥，另一个是私有密钥；公钥用作加密，私钥则用作解密。使用公钥把明文加密后所得的密文，只能用相对应的私钥才
能解密并得到原本的明文，最初用来加密的公钥不能用作解密。由于加密和解密需要两个不同的密钥，故被称为非对称加密；不同于加密和解密都使用同一个
密钥的对称加密。公钥可以公开，可任意向外发布；私钥不可以公开，必须由用户自行严格秘密保管，绝不透过任何途径向任何人提供，也不会透露给被信任
的要通信的另一方。

	基于公开密钥加密的特性，它还能提供数字签名的功能，使电子文件可以得到如同在纸本文件上亲笔签署的效果。公开密钥基础建设透过信任数字证书
认证机构的根证书、及其使用公开密钥加密作数字签名核发的公开密钥认证，形成信任链架构，已在TLS实现并在万维网的HTTP以HTTPS、在电子邮件
的SMTP以SMTPS或STARTTLS引入。

	在现实世界上可作比拟的例子是，一个传统保管箱，开门和关门都是使用同一条钥匙，这是对称加密；而一个公开的邮箱，投递口是任何人都可以寄信
进去的，这可视为公钥；而只有信箱主人拥有钥匙可以打开信箱，这就视为私钥。

	从DH算法我们可以看到，公钥-私钥组成的密钥对是非常有用的加密方式，因为公钥是可以公开的，而私钥是完全保密的，由此奠定了非对称加密的基础。

	非对称加密就是加密和解密使用的不是相同的密钥：只有同一个公钥-私钥对才能正常加解密。

	因此，如果小明要加密一个文件发送给小红，他应该首先向小红索取她的公钥，然后，他用小红的公钥加密，把加密文件发送给小红，此文件只能由小红的私钥
解开，因为小红的私钥在她自己手里，所以，除了小红，没有任何人能解开此文件。

	非对称加密的典型算法就是RSA算法，它是由Ron Rivest，Adi Shamir，Leonard Adleman这三个哥们一起发明的，所以用他们仨的姓的首字母缩写表示。

	非对称加密相比对称加密的显著优点在于，对称加密需要协商密钥，而非对称加密可以安全地公开各自的公钥，在N个人之间通信的时候：使用非对称加密只需要N个
密钥对，每个人只管理自己的密钥对。而使用对称加密需要则需要N*(N-1)/2个密钥，因此每个人需要管理N-1个密钥，密钥管理难度大，而且非常容易泄漏。

	既然非对称加密这么好，那我们抛弃对称加密，完全使用非对称加密行不行？也不行。因为非对称加密的缺点就是运算速度非常慢，比对称加密要慢很多。

	所以，在实际应用的时候，非对称加密总是和对称加密一起使用。假设小明需要给小红需要传输加密文件，他俩首先交换了各自的公钥，然后：

	1.小明生成一个随机的AES口令，然后用小红的公钥通过RSA加密这个口令，并发给小红；
	2.小红用自己的RSA私钥解密得到AES口令；
	3.双方使用这个共享的AES口令用AES加密通信。
	
	可见非对称加密实际上应用在第一步，即加密“AES口令”。这也是我们在浏览器中常用的HTTPS协议的做法，即浏览器和服务器先通过RSA交换AES口令，
接下来双方通信实际上采用的是速度较快的AES对称加密，而不是缓慢的RSA非对称加密。

	简单的说就是允许两名用户在公开媒体上交换信息以生成"一致"的、可以共享的密钥。换句话说，就是由甲方产出一对密钥（公钥、私钥），乙方依照甲方公钥产
生乙方密钥对（公钥、私钥）。以此为基线，作为数据传输保密基础，同时双方使用同一种对称加密算法构建本地密钥（SecretKey）对数据加密。这样，
在互通了本地密钥（SecretKey）算法后，甲乙双方公开自己的公钥，使用对方的公钥和刚才产生的私钥加密数据，同时可以使用对方的公钥和自己的私钥
对数据解密。不单单是甲乙双方两方，可以扩展为多方共享数据通讯，这样就完成了网络交互数据的安全通讯！该算法源于中国的同余定理——中国馀数定理。

流程分析：

1.甲方构建密钥对儿，将公钥公布给乙方，将私钥保留；双方约定数据加密算法；乙方通过甲方公钥构建密钥对儿，将公钥公布给甲方，将私钥保留。
2.甲方使用私钥、乙方公钥、约定数据加密算法构建本地密钥，然后通过本地密钥加密数据，发送给乙方加密后的数据；乙方使用私钥、甲方公钥、约定数据加
  密算法构建本地密钥，然后通过本地密钥对数据解密。
3.乙方使用私钥、甲方公钥、约定数据加密算法构建本地密钥，然后通过本地密钥加密数据，发送给甲方加密后的数据；甲方使用私钥、乙方公钥、约定数据加
  密算法构建本地密钥，然后通过本地密钥对数据解密。

一般公司这样做的。
甲乙公司各有自己的一套公钥私钥。
甲用乙公布的公钥加密，信息传递到乙，乙用自己的私钥解密。 --- 这一套是乙方的公私钥。
乙用甲公布的公钥加密，信息传递到甲，甲用自己的私钥解密。 --- 这一套是甲方的公私钥。
注：公钥长度远远小于私钥，公钥长度比较短，便于公钥保存。

【非对称加密算法 (RSA、DSA、ECC、DH)】
1. 简介
1.1 概念
非对称加密需要两个密钥：公钥 (publickey) 和私钥 (privatekey)。公钥和私钥是一对，如果用公钥对数据加密，那么只能用对应的私钥解密。如果用私钥对数据加密，只能
用对应的公钥进行解密。因为加密和解密用的是不同的密钥，所以称为非对称加密。

非对称加密算法的保密性好，它消除了最终用户交换密钥的需要。但是加解密速度要远远慢于对称加密，在某些极端情况下，甚至能比对称加密慢上1000倍。

1.2 特点
算法强度复杂、安全性依赖于算法与密钥但是由于其算法复杂，而使得加密解密速度没有对称加密解密的速度快。对称密码体制中只有一种密钥，并且是非公开的，如果要解密就得让对方知道密钥。所以保证其安全性就是保证密钥的安全，而非对称密钥体制有两种密钥，其中一个是公开的，这样就可以不需要像对称密码那样传输对方的密钥了。这样安全性就大了很多。

1.3 工作原理

     公钥A  -------------------->  私钥B
     私钥A  <--------------------  公钥B

      〸---------- 传递密文 -----------〸
      |                             |
使用公钥B对明文加密                使用私钥B对密文解密
      |                             ↓
      A                             B
      ↑                             |
使用私钥A对密文解密                使用公钥A对明文加密
      |                             |
      〸---------- 传递密文 -----------〸

(1) A 要向 B 发送信息，A 和 B 都要产生一对用于加密和解密的公钥和私钥。
(2) A 的私钥保密，A 的公钥告诉 B；B 的私钥保密，B 的公钥告诉 A。
(3) A 要给 B 发送信息时，A 用 B 的公钥加密信息，因为 A 知道 B 的公钥。
(4) A 将这个消息发给 B (已经用 B 的公钥加密消息)。
(5) B 收到这个消息后，B 用自己的私钥解密 A 的消息。其他所有收到这个报文的人都无法解密，因为只有 B 才有 B 的私钥。

1.4 主要算法
RSA、Elgamal、背包算法、Rabin、D-H、ECC (椭圆曲线加密算法)。使用最广泛的是 RSA 算法，Elgamal 是另一种常用的非对称加密算法。

1.5 应用场景
(1) 【信息加密】:收信者是唯一能够解开加密信息的人，因此收信者手里的必须是私钥。发信者手里的是公钥，其它人知道公钥没有关系，因为其它人发来的信息对收信者没有意义。

(2) 【登录认证】:客户端需要将认证标识传送给服务器，此认证标识 (可能是一个随机数) 因为其它客户端可以知道，因此需要用私钥加密，客户端保存的是私钥。服务器端保存的是
    公钥，其它服务器知道公钥没有关系，因为客户端不需要登录其它服务器。

(3) 【数字签名】：数字签名是为了表明信息没有受到伪造，确实是信息拥有者发出来的，附在信息原文的后面。就像手写的签名一样，具有不可抵赖性和简洁性。
	简洁性：对信息原文做哈希运算，得到消息摘要，信息越短加密的耗时越少。
	不可抵赖性：信息拥有者要保证签名的唯一性，必须是唯一能够加密消息摘要的人，因此必须用私钥加密(就像字迹他人无法学会一样)得到签名。如果用公钥，那每个人都可以伪
	         造签名了。

(4) 【数字证书】：
	问题起源：对1和3，发信者怎么知道从网上获取的公钥就是真的？没有遭受中间人攻击？这样就需要第三方机构来保证公钥的合法性，这个第三方机构就
	是 CA (Certificate Authority)，证书中心。
	CA 用自己的私钥对信息原文所有者发布的公钥和相关信息进行加密，得出的内容就是数字证书。
	信息原文的所有者以后发布信息时，除了带上自己的签名，还带上数字证书，就可以保证信息不被篡改了。信息的接收者先用 CA给的公钥解出信息所有者的公钥，这样可以保证
	信息所有者的公钥是真正的公钥，然后就能通过该公钥证明数字签名是否真实了。

2. RSA算法
2.1 简介
	RSA 是目前最有影响力的公钥加密算法，该算法基于一个十分简单的数论事实：将两个大素数相乘十分容易，但想要对其乘积进行因式分解却极其困难，因此可以将乘积公开作
为加密密钥，即公钥，而两个大素数组合成私钥。公钥是可发布的供任何人使用，私钥则为自己所有，供解密之用。

2.2 工作流程
	A 要把信息发给 B 为例，确定角色：A 为加密者，B 为解密者。首先由 B 随机确定一个 KEY，称之为私钥，将这个 KEY 始终保存在机器 B 中而不发出来；然后，由
	这个 KEY 计算出另一个 KEY，称之为公钥。这个公钥的特性是几乎不可能通过它自身计算出生成它的私钥。接下来通过网络把这个公钥传给 A，A 收到公钥后，利用公钥
	对信息加密，并把密文通过网络发送到 B，最后 B 利用已知的私钥，就能对密文进行解码了。以上就是 RSA 算法的工作流程。

2.3 运算速度
	由于进行的都是大数计算，使得 RSA 最快的情况也比 DES 慢上好几倍，无论是软件还是硬件实现。速度一直是 RSA 的缺陷。一般来说只用于少量数据加密。RSA 的速度
	是对应同样安全级别的对称密码算法的1/1000左右。

	比起 DES 和其它对称算法来说，RSA 要慢得多。实际上一般使用一种对称算法来加密信息，然后用 RSA 来加密比较短的公钥，然后将用 RSA 加密的公钥和用对称算法加
	密的消息发送给接收方。

	这样一来对随机数的要求就更高了，尤其对产生对称密码的要求非常高，否则的话可以越过 RSA 来直接攻击对称密码。

2.4 公钥传递安全
	和其它加密过程一样，对 RSA 来说分配公钥的过程是非常重要的。分配公钥的过程必须能够抵挡中间人攻击。假设 A 交给 B 一个公钥，并使 B 相信这是A 的公钥，并
	且 C 可以截下 A 和 B 之间的信息传递，那么 C 可以将自己的公钥传给 B，B 以为这是 A 的公钥。C 可以将所有 B 传递给 A 的消息截下来，将这个消息用自己的
	密钥解密，读这个消息，然后将这个消息再用 A 的公钥加密后传给 A。理论上 A 和 B 都不会发现 C 在偷听它们的消息，今天人们一般用数字认证来防止这样的攻击。

2.5 攻击
(1) 针对 RSA 最流行的攻击一般是基于大数因数分解。1999年，RSA-155 (512 bits) 被成功分解，花了五个月时间（约8000 MIPS 年）和224 CPU hours 在一台
有3.2G 中央内存的 Cray C916计算机上完成。

RSA-158 表示如下：

3950587458326514452641976780061448199602077646030493645413937605157935562652945068360972784246821953509354430587049025199
5655335710209799226484977949442955603= 3388495837466721394368393204672181522815830368604993048084925840555281177 × 116588
23406671259903148376558383270818131012258146392600439520994131344334162924536139

2009年12月12日，编号为 RSA-768 (768 bits, 232 digits) 数也被成功分解。这一事件威胁了现通行的1024-bit 密钥的安全性，普遍认为用户应尽快升级到2048-bit 或以上。

RSA-768表示如下：

12301866845301177551304949583849627207728535695953347921973224521517264005072636575187452021997864693899564749427740638459
25192557326303453731548268507917026122142913461670429214311602221240479274737794080665351419597459856902143413= 3347807169
8956898786044169848212690817704794983713768568912431388982883793878002287614711652531743087737814467999489 × 3674604366679
959042824463379962795263227915816434308764267  6032283815739666511279233373417143396810270092798736308917

(2) 秀尔算法

量子计算里的秀尔算法能使穷举的效率大大的提高。由于 RSA 算法是基于大数分解 (无法抵抗穷举攻击)，因此在未来量子计算能对 RSA 算法构成较大的威胁。一个拥有 N 量子位
的量子计算机，每次可进行2^N 次运算，理论上讲，密钥为1024位长的 RSA 算法，用一台512量子比特位的量子计算机在1秒内即可破解。

【使用到的类和相关参数：】
-KeyPairGenerator：密钥对生成器
-KeyPair：密钥对
-PublixKey：公钥
-PrivateKey ：私钥
-KeyFactory：作用是生成密钥（包括公钥和私钥）

-generatePublic()方法用来生成公钥
-generatePrivate()方法用来生成私钥

-X509EncodedKeySpec：继承EncodedKeySpec类 ，以编码格式来表示公钥
-PKCS8EncodedKeySpec：继承EncodedKeySpec类，以编码格式来表示私钥
-DHPublicKey：是PublicKey的某种具体的形式
-DHParameterSpec：随从着DH算法来使用的参数的集合
-KeyAgreement：该类提供密钥一致性（或者密钥交换）协议的功能
-SecretKey：构建的本地秘钥
-Cipher：提供加解密的功能

Java标准库提供了RSA算法的实现，示例代码如下：
*/
/*
import java.math.BigInteger;

//import java.security.*;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;

import javax.crypto.Cipher;

import org.apache.commons.codec.binary.Base64;

public class 非对称加密算法 {
    public static void main(String[] args) throws Exception {
   	 System.out.println("非对称加密算法");
        // 明文:
        byte[] plain = "Hello, encrypt use RSA".getBytes("UTF-8");
        // 创建公钥／私钥对:
        Person0 alice = new Person0("Alice");
        // 用Alice的公钥加密:
        byte[] pubk = alice.getPublicKey();
        System.out.println(String.format("public key: %x", new BigInteger(1, pubk)));
        byte[] encrypted = alice.encrypt(plain);
        System.out.println(String.format("encrypted: %x", new BigInteger(1, encrypted)));
        // 用Alice的私钥解密:
        byte[] prik = alice.getPrivateKey();
        System.out.println(String.format("private key: %x", new BigInteger(1, prik)));
        byte[] decrypted = alice.decrypt(encrypted);
        System.out.println(new String(decrypted, "UTF-8"));
    }
}

class Person0 {
    String name;
    // 私钥:
    PrivateKey prik;
    // 公钥:
    PublicKey pubk;

    public Person0(String name) throws GeneralSecurityException {
        this.name = name;
        // 生成公钥／私钥对:
        KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA");
        kpGen.initialize(1024);
        KeyPair kp = kpGen.generateKeyPair();
        this.prik = kp.getPrivate();
        this.pubk = kp.getPublic();
    }

    // 把私钥导出为字节
    public byte[] getPrivateKey() {
        return this.prik.getEncoded();
    }

    // 把公钥导出为字节
    public byte[] getPublicKey() {
        return this.pubk.getEncoded();
    }

    // 用公钥加密:
    public byte[] encrypt(byte[] message) throws GeneralSecurityException {
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, this.pubk);
        return cipher.doFinal(message);
    }

    // 用私钥解密:
    public byte[] decrypt(byte[] input) throws GeneralSecurityException {
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, this.prik);
        return cipher.doFinal(input);
    }
}
*/
/*
RSA的公钥和私钥都可以通过getEncoded()方法获得以byte[]表示的二进制数据，并根据需要保存到文件中。要从byte[]数组恢复公钥或
私钥，可以这么写：

byte[] pkData = ...
byte[] skData = ...
KeyFactory kf = KeyFactory.getInstance("RSA");
// 恢复公钥:
X509EncodedKeySpec pkSpec = new X509EncodedKeySpec(pkData);
PublicKey pk = kf.generatePublic(pkSpec);
// 恢复私钥:
PKCS8EncodedKeySpec skSpec = new PKCS8EncodedKeySpec(skData);
PrivateKey sk = kf.generatePrivate(skSpec);
以RSA算法为例，它的密钥有256/512/1024/2048/4096等不同的长度。长度越长，密码强度越大，当然计算速度也越慢。

如果修改待加密的byte[]数据的大小，可以发现，使用512bit的RSA加密时，明文长度不能超过53字节，使用1024bit的RSA加密时，明文长度不能超
过117字节，这也是为什么使用RSA的时候，总是配合AES一起使用，即用AES加密任意长度的明文，用RSA加密AES口令。

此外，只使用非对称加密算法不能防止中间人攻击。
*/

//可以看出RSA算法是支持私钥加密、公钥解密和公钥加密、私钥解密的算法。RSA也是唯一被广泛接受并实现的非对称加密算法，从某种程度来说，
//RSA已经成为非对称加密算法的一个标准了。 
import org.apache.commons.codec.binary.Base64;
 
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
 
import static java.lang.System.out;
 
/**
 * <p>Title: JdkRsa</p >
 * <p>Description: RSA非对称加密算法实现 </p >
 * <p>Company: http://www.agree.com</p >
 * <p>Project: security</p >
 *
 * @author <a href="mailto:weiqi@agree.com.cn">WEIQI</a>
 * @version 1.0
 * @date 2022-04-27 21:38
 */
public class 非对称加密算法 {
    
    private static final String SRC = "I'm RSA encryption algorithm";
    
    public static void main(String[] args) {
        jdkRsa();
    }
    
    /**
     * JDK-RSA算法实现
     * 
     * @author: <a href="mailto:weiqi@agree.com.cn">WEIQI</a>
     * @date: 2022-04-28 0:15
     */
    private static void jdkRsa() {
        try {
            // 初始化密钥
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
            keyPairGenerator.initialize(512);
            KeyPair keyPair = keyPairGenerator.generateKeyPair();
            //RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();
            PublicKey rsaPublicKey =  keyPair.getPublic();
            //RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();
            PrivateKey rsaPrivateKey = keyPair.getPrivate();
            
            out.println("public key is : " + Base64.encodeBase64String(rsaPublicKey.getEncoded()));
            out.println("private key is : " + Base64.encodeBase64String(rsaPrivateKey.getEncoded()));
            
            // 私钥加密，公钥解密 -- 加密
            PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(rsaPrivateKey.getEncoded());
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.ENCRYPT_MODE, privateKey);
            byte[] result = cipher.doFinal(SRC.getBytes(StandardCharsets.UTF_8));
            out.println("私钥加密，公钥解密 -- 加密 : " + Base64.encodeBase64String(result));
            
            // 私钥加密，公钥解密 -- 解密
            X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(rsaPublicKey.getEncoded());
            keyFactory = KeyFactory.getInstance("RSA");
            PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);//将公钥转换成PublicKey对象
            cipher.init(Cipher.DECRYPT_MODE, publicKey);
            result = cipher.doFinal(result);
            out.println("私钥加密，公钥解密 -- 解密 : " + new String(result));
    
            // 公钥加密，私钥解密 -- 加密
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            byte[] res = cipher.doFinal(SRC.getBytes(StandardCharsets.UTF_8));
            out.println("公钥加密，私钥解密 -- 加密 : " + Base64.encodeBase64String(res));
            // 公钥加密，私钥解密 -- 解密
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            res = cipher.doFinal(res);
            out.println("公钥加密，私钥解密 -- 解密 : " + new String(res));
        } catch (NoSuchAlgorithmException | InvalidKeySpecException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) {
            e.printStackTrace();
        }
    }
}